/*****************************************************************************//**
 * Arduino library to control LPD8806-based RGB LED Strips
 * (c) Adafruit industries
 * MIT license
 * ********************************************************************************/

/*****************************************************************************//**
 * This library has been modified to fix the red and green issue with the stripes
 * used by "MikroKopter-Forum" Users, because the design of these stripes is
 * different and red and green are inverted !!
 * Additionally this library has been modified and extended to provide light
 * sequence requirements on multikopter.
 * Magomora
 *
 * Rework by Hoppel 2012
 *
 * Changelog:
 * ------------------------------------------------------
 * 07/12/2012
 * -Comments
 * -rewrite setPixel*
 * -redefine packed 32 Bit RGB  Value
 * 15/12/2012
 * -remove parallelPixel
 * -use rgb color structure
 * -remove soft spi
 * ****************************************************************************/


/* INCLUDES ******************************************************************/
#include "SPI.h"
#include "LPD8806.h"
#include "colors.h"

/* Contructors ***************************************************************/


/**
 * @brief Constructor for Hardware SPI
 * @param n Number of LEDs of all Stripe parts
 * @param stripes Number of Pieces in which the stripe is cutted.
 */
LPD8806::LPD8806(uint16_t n, uint8_t stripes)
{
	pPixels = NULL;
	updateLength(n);
	nStripes = stripes;
}

/**
 * @brief Starts the SPI.
 */
void LPD8806::begin(void)
{
	SPI.begin();
	SPI.setBitOrder(MSBFIRST);
	SPI.setDataMode(SPI_MODE0);
    //  SPI.setClockDivider(SPI_CLOCK_DIV8);  // 2 MHz
	SPI.setClockDivider(SPI_CLOCK_DIV2);  // 8 MHz
	// SPI bus is run at 2MHz.  Although the LPD8806 should, in theory,
	// work up to 20MHz, the unshielded wiring from the Arduino is more
	// susceptible to interference.  Experiment and see what you get.

	SPDR = 0; // 'Prime' the SPI bus with initial latch (no wait)

}



/**
 * Change strip length (see notes with empty constructor, above)
 * @param n Number of LEDs from entire Stripe
 */
void LPD8806::updateLength(uint16_t n)
{
	if (pPixels != NULL)
		free (pPixels); // Free existing data (if any)
	numLEDs = n;
	n *= 3; // 3 bytes per pixel
	if (NULL != (pPixels = (uint8_t *) malloc(n + 1)))
	{ // Alloc new data
		memset(pPixels, 0x80, n); // Init to RGB 'off' state
		pPixels[n] = 0;        // Last byte is always zero for latch
		pPixelBak = pPixels;  //save pointer
	}
	else
		numLEDs = 0;        // else malloc failed
	// 'begun' state does not change -- pins retain prior modes
}

/**
 * Gets the number of LEDs in the entire stripe.
 * @return Number of LEDs
 */
uint16_t LPD8806::getNumPixels(void)
{
	return numLEDs;
}

/**
 * @brief This is how data is pushed to the strip.  Unfortunately, the company
 * that makes the chip didnt release the protocol document or you need
 * to sign an NDA or something stupid like that, but we reverse engineered
 * this from a strip controller and it seems to work very nicely!
 */
void LPD8806::show(void)
{
	uint16_t i, n3 = numLEDs * 3 + 1; // 3 bytes per LED + 2 for latch

		for (i = 0; i < n3; i++)
		{
			while (!(SPSR & (1 << SPIF)))
				; // Wait for prior byte out
			SPDR = pPixels[i];           // Issue new byte
		}
		while (!(SPSR & (1 << SPIF)))
			; // Wait for prior byte out
		SPDR = 0;           // Issue new byte
}


/**
 * @brief Writes the r, g, b Values into Color Buffer
 * @param position Position of Pixel/Led on Stripe 
 * @param rgb Value for Red, Green, Blue
 */
void LPD8806::writeArray(uint8_t position, rgb_t rgb)
{
	uint8_t *p = &pPixels[position * 3];
	*p++ = rgb.r | 0x80; // LPD8806 color order is GRB,
	*p++ = rgb.g | 0x80; // not the more common RGB,
	*p++ = rgb.b | 0x80; // so the order here is intentional; don't "fix"
}



/**
 * @brief Sets the Color of a Led at one or more stripe
 * @param pos LED Position at a Rigger
 * @param rgb Value for Red, Green, Blue
 * @param stripe Select stripe to effect
 */
void LPD8806::setPixelColor(uint16_t pos, rgb_t rgb, uint8_t stripe)
{
	int i;
	uint8_t position; // calculated Pixel position
	uint16_t StripeLength = numLEDs / nStripes; //length of a stripe on one rigger

	switch (stripe)
	{
	case 1:
		position = pos - 1;
		break;
	case 2:
		position = (2 * StripeLength) - pos; // RGB Sphere
//        position = StripeLength + pos;
		break;
	case 3:
		position = (StripeLength * 2) + pos - 1;
		break;
	case 4:
		position = (4 * StripeLength) - pos; // RGB Sphere
//	position = (StripeLength * 3) + pos;
		break;
	case 5:
		position = (StripeLength * 4) + pos - 1;
		break;
	case 6:
		position = (6 * StripeLength) - pos; // RGB Sphere
//	position = (StripeLength * 5) + pos;
		break;
	case 7:
		position = (StripeLength * 6) + pos - 1;
		break;
	case 8:
		position = (8 * StripeLength) - pos; // RGB Sphere
//	position = (StripeLength * 7) + pos;
		break;
	default:
		return;
	}

	if (pos < numLEDs)
	{ 
	    writeArray(position , rgb);
	}
}

/**
 * @brief Sets the color for a entire Stripe piece with separated RGB Values.
 * @param r Value for Red, Green, Blue
 * @param stripe Select stripe to effect
 */
void LPD8806::setStripeColor(rgb_t rgb, uint8_t stripe)
{
	uint8_t pos; // calculated Pixel position
	uint16_t StripeLength = numLEDs / nStripes; //length of a stripe
	
	switch (stripe)
	{
	case 1:
		for (pos = 0; pos < StripeLength; pos++)
				writeArray(pos, rgb);			
		break;
	case 2:
		for (pos = StripeLength * (stripe - 1); pos < (StripeLength * stripe); pos++)
				writeArray(pos, rgb);			
		break;
	case 3:
		for (pos = StripeLength * (stripe - 1); pos < (StripeLength * stripe); pos++)
				writeArray(pos, rgb);
		break;
	case 4:
		for (pos = StripeLength * (stripe - 1); pos < (StripeLength * stripe); pos++)
				writeArray(pos, rgb);
		break;
	case 5:
		for (pos = StripeLength * (stripe - 1); pos < (StripeLength * stripe); pos++)
				writeArray(pos, rgb);
		break;
	case 6:
		for (pos = StripeLength * (stripe - 1); pos < (StripeLength * stripe); pos++)
				writeArray(pos, rgb);
		break;
	case 7:
		for (pos = StripeLength * (stripe - 1); pos < (StripeLength * stripe); pos++)
				writeArray(pos, rgb);
		break;
	case 8:
		for (pos = StripeLength * (stripe - 1); pos < (StripeLength * stripe); pos++)
				writeArray(pos, rgb);
		break;
	default: break;
	}
}

/**
 * @brief set a Pixel Color at any Position in stripe.
 * @param pos Position
 * @param color Color Value as rgb_t struct
 */
void LPD8806::setPixelColor(uint8_t pos, rgb_t color)
{
   writeArray(pos, color);
}


/**
 * @brief gets the current Color value from a Pixel
 * @param pos Position of Pixel
 * @return RGB Value as rgb_t struct
 */
rgb_t LPD8806::getPixelColor(uint8_t pos)
{
   rgb_t rgb;
   uint8_t *p = &pPixels[pos * 3]; // get pointer from LED
   rgb.r = *p++ & 0x7f; 
   rgb.g = *p++ & 0x7f; 
   rgb.b = *p & 0x7f; 

   return rgb;
}

/**
 * @brief Clear the stripe
 * @param stripeMask Defines Stripe Piece which has to clear
 */
void LPD8806::clear(void)
{
	int i;
	rgb_t c = {0,0,0};
	for (i=1; i<= nStripes; i++)
		setStripeColor(c, i);
}

/**
 * @brief converts a 32Bit packed RGB Color into
 * used structure.
 * @param color 32 Bit Color Value
 * @return Color in rgb_t structure
 */
rgb_t LPD8806::convertRGB(uint32_t color)
{
   rgb_t c;
   c.r = (color >> 17);
   c.g = (color >> 9);
   c.b = (color >> 1);	
}

